package net.towee.server.ftp;

import java.util.Arrays;
import java.util.List;

import net.towee.model.account.AccountGroup;
import net.towee.model.repo.AttachmentIdentity;
import net.towee.model.repo.DocumentData;
import net.towee.model.repo.DocumentNode;
import net.towee.model.repo.NodeData;
import net.towee.model.repo.TreeNode;
import net.towee.server.ftp.files.AttachmentFtpFile;
import net.towee.server.ftp.files.AttachmentsListFtpFile;
import net.towee.server.ftp.files.DocumentDataFtpFile;
import net.towee.server.ftp.files.DocumentNodeFtpFile;
import net.towee.server.ftp.files.RootFtpFile;
import net.towee.server.ftp.files.TreeNodeFtpFile;
import net.towee.server.persistence.AttachmentStore;
import net.towee.server.persistence.DiscussionStore;
import net.towee.server.persistence.DocumentStore;
import net.towee.shared.exceptions.PersistenceException;

import org.apache.ftpserver.filesystem.nativefs.impl.NativeFtpFile;
import org.apache.ftpserver.ftplet.FileSystemView;
import org.apache.ftpserver.ftplet.FtpException;
import org.apache.ftpserver.ftplet.FtpFile;

import com.google.common.collect.ImmutableMap;
import com.google.gwt.thirdparty.guava.common.base.Joiner;

/**
 * File system implementation for towee store
 * 
 * @author c58
 */
public class ToweeFileSystemView implements FileSystemView {
	private final ImmutableMap<String, AccountGroup> groupsMap;
	private final DocumentStore docStore;
	private final AttachmentStore attStore;
	private final DiscussionStore discStore;

	// Current showed ftp file
	private String currentDir = "/";
	private NodeData currentNode;
	private boolean isAttachment;

	public ToweeFileSystemView(List<AccountGroup> groupsList, DocumentStore docStore,
			AttachmentStore attStore, DiscussionStore discStore) {
		this.docStore = docStore;
		this.attStore = attStore;
		this.discStore = discStore;

		// Create map of groups where key is group alias
		ImmutableMap.Builder<String, AccountGroup> mapBuilder = new ImmutableMap.Builder<String, AccountGroup>();
		for (AccountGroup g : groupsList) {
			mapBuilder.put(g.getGroupIdentity().getAlias(), g);
		}

		// Build immutable map
		groupsMap = mapBuilder.build();
	}

	@Override
	public FtpFile getHomeDirectory() throws FtpException {
		return new RootFtpFile(groupsMap.values(), docStore, attStore, discStore);
	}

	@Override
	public FtpFile getWorkingDirectory() throws FtpException {
		if (currentDir.equals("/"))
			return getHomeDirectory();
		else
			return getFile(currentDir);
	}

	@Override
	public boolean changeWorkingDirectory(String dir) throws FtpException {
		String path = NativeFtpFile.getPhysicalName("/", currentDir, dir, true);
		List<String> parts = Arrays.asList(path.split("/"));

		// If its home directory
		if (parts.size() == 0) {
			currentDir = "/";
			currentNode = null;
			return true;
		}
		// If its group root directory
		else {
			// Check group name
			if (!groupsMap.containsKey(parts.get(1)))
				return false;
			
			if(parts.size() == 2) {
				currentDir = path;
				currentNode = null;
				return true;
			}

			// Get last field
			int shift = 1;
			String lastField = parts.get(parts.size() - 1);

			if (lastField.equals("attachments"))
				shift++;

			// Get path to node
			String reqPath = "/"+Joiner.on("/").join(parts.subList(2, parts.size() - shift));
			reqPath += (reqPath.length() > 1) ? "/" : "";
			
			// Try to get node
			NodeData node = docStore.getNodeByPath(
					reqPath,
					parts.get(parts.size() - shift), 
					groupsMap.get(parts.get(1)).getGroupIdentity().getId());
			
			System.out.println(node.getPath()+"  "+shift);
			
			// Validate node
			if (node == null || (shift == 2 && !(node instanceof DocumentNode) ))
				return false;

			// Set if this is attachment folder
			if (shift == 2)
				isAttachment = true;
			else
				isAttachment = false;

			// Set current dir
			currentDir = path;
			currentNode = node;
			return true;
		}
	}

	@Override
	public FtpFile getFile(String file) throws FtpException {
		// Normolize path to file
		String path = NativeFtpFile.getPhysicalName("/", currentDir, file, true);
		List<String> parts = Arrays.asList(path.split("/"));

		// If its home directory
		if (parts.size() == 0)
			return getHomeDirectory();

		// Check group name
		if (!groupsMap.containsKey(parts.get(1)))
			throw new FtpException();

		// If its group root directory
		if (parts.size() == 2) {
			// Check group name
			if (!groupsMap.containsKey(parts.get(1)))
				throw new FtpException();

			return new TreeNodeFtpFile(null, groupsMap.get(parts.get(1)), docStore, attStore, discStore);
		}

		// Receive attachments list of current node
		if (isAttachment)
			if (path.equals(currentDir))
				return new AttachmentsListFtpFile((DocumentNode) currentNode, docStore, attStore);
			else if(path.startsWith(currentDir+"/")) {
				// Get attachment name
				String fileName = path.substring( currentDir.length()+1 );
				
				// Try to get document
				DocumentData doc;				
				try {
					doc = docStore.getDocument( ((DocumentNode) currentNode).getDocumentIdentity().getId(), "attach");
				} catch (PersistenceException e){
					throw new FtpException();
				}
				
				// Find attachment with given name
				for(AttachmentIdentity id : doc.getAttachments().getAttachmentList() ) {
					if( !id.getName().equals( fileName )) continue;
					return new AttachmentFtpFile((DocumentNode) currentNode, id, attStore);
				}
				
				// File with given name not found
				throw new FtpException();
			}

		// Receive document data or document node
		if (currentNode instanceof DocumentNode) {
			if (path.startsWith(currentDir))
				if (path.endsWith(".html"))
					return new DocumentDataFtpFile((DocumentNode)currentNode, docStore, discStore);
				else
					return new DocumentNodeFtpFile((DocumentNode) currentNode,
							groupsMap.get(parts.get(1)), docStore, attStore, discStore);
			else
				throw new FtpException();
		
		// Receive simple tree node (directory)
		} else 
			return new TreeNodeFtpFile((TreeNode)currentNode, groupsMap.get(parts.get(1)), docStore, attStore, discStore);
	}

	@Override
	public boolean isRandomAccessible() throws FtpException {
		return true;
	}

	@Override
	public void dispose() {
	}

}
